package cn.redragon.soa.common.util.jwt;


import static java.util.Collections.emptyMap;
import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;

import cn.redragon.soa.common.constant.AuthConstant;
import cn.redragon.soa.common.dto.JwtSecurityProperty;
import cn.redragon.soa.common.util.DateUtil;
import cn.redragon.soa.common.util.JsonUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.stereotype.Component;

@Component
@ConditionalOnBean(JwtSecurityProperty.class)
public class RsaJwtTokenCreator {

    private final JwtSecurityProperty jwtSecurityProperty;

    public RsaJwtTokenCreator(JwtSecurityProperty jwtSecurityProperty) {
        this.jwtSecurityProperty = jwtSecurityProperty;
    }

    /**
     * Create Json Web Token subject(username) and roles
     */
    public String createToken(String subject, Collection<String> roles) {
        return createToken(subject, roles, emptyMap());
    }

    /**
     * Create Json Web Token subject(username) and roles with extra claims
     */
    public String createToken(String subject, Collection<String> roles, Map<String, Object> extraClaims) {
        JwtBuilder builder = getJwtBuilder(subject, roles, extraClaims);

        return builder.compact();
    }

    public String createToken(String subject, Collection<String> realmRoles, Collection<String> resourceRoles, Map<String, Object> extraClaims) {
        JwtBuilder builder = getJwtBuilder(subject, resourceRoles, extraClaims);
        if (isNotEmpty(realmRoles)) {
            JsonNode rolesStr = JsonUtil.objectToJson(realmRoles);
            ObjectNode roleNode = JsonUtil.newJSONObject();
            roleNode.set(AuthConstant.CLAIM_WRAPPED_ROLES, rolesStr);
        }
        return builder.compact();
    }

    private JwtBuilder getJwtBuilder(final String subject, final Collection<String> resourceRoles, final Map<String, Object> extraClaims) {
        LocalDateTime expirationLocalTime = LocalDateTime.now().plusSeconds(jwtSecurityProperty.getExpiration());
        Date expirationDate = DateUtil.localDateTimeToDate(expirationLocalTime);

        JwtBuilder builder = Jwts.builder()
            .signWith(jwtSecurityProperty.getTokenGenerateKey())
            .setIssuer(jwtSecurityProperty.getIssuer())
            .setIssuedAt(new Date())
            .setExpiration(expirationDate);


        if (isNotEmpty(resourceRoles)) {
            JsonNode rolesStr = JsonUtil.objectToJson(resourceRoles);
            ObjectNode roleNode = JsonUtil.newJSONObject();
            roleNode.set(AuthConstant.CLAIM_WRAPPED_ROLES, rolesStr);

            ObjectNode resourceNode = JsonUtil.newJSONObject();
            resourceNode.set(jwtSecurityProperty.getClientId(), roleNode);

        }

        for (Map.Entry<String, Object> extra : extraClaims.entrySet()) {
            builder.claim(extra.getKey(), extra.getValue());
        }
        return builder;
    }

}
